package com.capgemini.cn.authority.core.automation;

import com.capgemini.cn.authority.data.entity.Asserts;
import com.capgemini.cn.authority.data.entity.AssertsGroup;
import com.capgemini.cn.authority.data.entity.Authorities;
import com.capgemini.cn.authority.data.entity.AuthoritiesType;
import com.capgemini.cn.authority.data.enums.AuthorityType;
import com.capgemini.cn.authority.data.repository.AssertsGroupRepository;
import com.capgemini.cn.authority.data.repository.AssertsRepository;
import com.capgemini.cn.authority.data.repository.AuthoritiesRepository;
import com.capgemini.cn.authority.data.repository.AuthoritiesTypeRepository;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;

/**
 * Created at 2017/10/9
 *
 * @author Andriy
 */
@Service
public class AuthorizeRegisterServiceImpl implements AuthorizeRegisterService {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    @Setter
    private AssertsGroupRepository assertsGroupRepository;

    @Autowired
    @Setter
    private AuthoritiesTypeRepository authoritiesTypeRepository;

    @Autowired
    @Setter
    private AssertsRepository assertsRepository;

    @Autowired
    @Setter
    private AuthoritiesRepository authoritiesRepository;

    private ObjectMapper objectMapper = new ObjectMapper();

    @Transactional
    @Override
    public Object[] initialization() {
        Object[] result = new Object[3];
        // 校验默认资源组是否已经存在，如果没有则进行创建
        AssertsGroup assertsGroup = this.assertsGroupRepository.findByAssertsGroupNameEquals(AuthorizeDefaultValues.AUTHORIZE_DEFAULT_ASSET_GROUP_NAME.value());
        if (ObjectUtils.isEmpty(assertsGroup)) {
            assertsGroup = new AssertsGroup();
            assertsGroup.setAssertsGroupName(AuthorizeDefaultValues.AUTHORIZE_DEFAULT_ASSET_GROUP_NAME.value());
            assertsGroup.setSystemCreated(true);    // 标识为系统自动创建
            assertsGroup = this.assertsGroupRepository.saveAndFlush(assertsGroup);
        }
        // 校验默认权限类型是否已经存在，如果没有则进行创建
        AuthoritiesType authoritiesType = this.authoritiesTypeRepository.findByTypeNameEquals(AuthorityType.HTTP.value());
        if (ObjectUtils.isEmpty(authoritiesType)) {
            authoritiesType = new AuthoritiesType();
            authoritiesType.setTypeName(AuthorityType.HTTP);
            authoritiesType = this.authoritiesTypeRepository.saveAndFlush(authoritiesType);
        }
        // 校验默认权限是否已经存在，如果没有则进行创建
        // TODO 实际上目前系统中的默认权限已经完全无用，待程序稳定后可以删除相关初始化代码
        Authorities authorities = this.authoritiesRepository.findByAuthorityCodeEquals(AuthorizeDefaultValues.AUTHORIZE_DEFAULT_CODE.value());
        if (ObjectUtils.isEmpty(authorities)) {
            authorities = new Authorities();
            authorities.setAuthoritiesType(authoritiesType);
            authorities.setAuthorityName(AuthorizeDefaultValues.AUTHORIZE_DEFAULT_NAME.value());
            authorities.setAuthorityCode(AuthorizeDefaultValues.AUTHORIZE_DEFAULT_CODE.value());
            authorities.setSystemCreated(true); // 标识为系统自动创建
            authorities = this.authoritiesRepository.saveAndFlush(authorities);
        }
        // 组装返回值
        result[0] = assertsGroup;
        result[1] = authoritiesType;
        result[2] = authorities;
        return result;
    }

    /**
     * 创建权限标识中的自定义资源分组并关联对应的权限
     *
     * @param assertsGroup    系统资源所属默认资源组
     * @param authoritiesType 权限类型
     * @param authorizeMap    权限注册列表
     * @return 返回一个java.util.List对象，包含所有被注册过的权限编码集合
     */
    private List<String> individuationHandler(final AssertsGroup assertsGroup, final AuthoritiesType authoritiesType, final List<AuthorizeEntity> authorizeMap) {
        final List<String> authorityCodeList = new ArrayList<>();
        for (AuthorizeEntity authEntity : authorizeMap) {
            // 处理自定义权限数据，根据权限编码字段查询权限数据
            Authorities authorities = this.authoritiesRepository.findByAuthorityCodeEquals(authEntity.getCode());
            // 权限如果不存在则需创建一个新的资源
            if (ObjectUtils.isEmpty(authorities)) {
                authorities = new Authorities();
                authorities.setAuthoritiesType(authoritiesType);
                authorities.setAuthorityCode(authEntity.getCode());
                authorities.setAuthorityName(authEntity.getCode()); // 系统自动注册时权限名和权限编码保持一致
                authorities.setSystemCreated(true); // 标识为系统自动创建
                this.authoritiesRepository.saveAndFlush(authorities);
            }
            // 放入列表容器中以标识当前权限将会被注册
            authorityCodeList.add(authorities.getAuthorityCode());
            // 处理自定义资源组数据
            if (!ObjectUtils.isEmpty(authEntity.getGroups())) {
                // 创建个性化资源组并与对应权限进行关联
                AssertsGroup customAssetsGroup = this.assertsGroupRepository.findByAssertsGroupNameEquals(authEntity.getGroups());
                if (ObjectUtils.isEmpty(customAssetsGroup)) {
                    customAssetsGroup = new AssertsGroup();
                    customAssetsGroup.setAssertsGroupName(authEntity.getGroups());
                    customAssetsGroup.setSystemCreated(true);   // 标识为系统自动创建
                }
                customAssetsGroup = this.associate(customAssetsGroup, authorities);
                this.assertsGroupRepository.saveAndFlush(customAssetsGroup);
            } else {
                // 将默认资源组与权限进行关联
                AssertsGroup associateAssertsGroup = this.associate(assertsGroup, authorities);
                this.assertsGroupRepository.saveAndFlush(associateAssertsGroup);
            }
        }
        return authorityCodeList;
    }

    @Transactional
    @Override
    public List[] register(final AssertsGroup assertsGroup, final Authorities authorities, final List<AuthorizeEntity> authorizeMap) {
        List[] result = new List[2];
        final List<Asserts> assetList = new ArrayList<>();
        final List<String> assetValueList = new ArrayList<>();

        // 将自定义资源组与对应的权限进行关联，自定义权限类型与系统默认权限类型保持一致
        final List<String> authorityCodeList = this.individuationHandler(assertsGroup, authorities.getAuthoritiesType(), authorizeMap);
        // TODO 默认权限已经无用，故注释掉该段代码
//        authorityCodeList.add(authorities.getAuthorityCode());  // 将默认权限编码加入到已注册列表中
        // 处理权限资源数据
        for (AuthorizeEntity authEntity : authorizeMap) {
            // 根据资源值字段查询资源数据
            Asserts asserts = this.assertsRepository.findByAssertValueEquals(authEntity.getAssetValue());
            // 资源如果不存在则需创建一个新的资源
            asserts = this.assetHandler(assertsGroup, assetList, authEntity, asserts);
            // 放入列表容器中以标识当前资源将会被注册
            assetValueList.add(asserts.getAssertValue());
        }
        // 将所有新增和变更过的资源数据保存至数据库
        if (!ObjectUtils.isEmpty(assetList)) {
            this.assertsRepository.save(assetList);
            this.assertsRepository.flush();
        }
        // 组装返回结果
        result[0] = assetValueList;
        result[1] = authorityCodeList;
        return result;
    }

    /**
     * 关联资源组与权限
     *
     * @param assertsGroup 资源组
     * @param authorities  权限
     */
    private AssertsGroup associate(AssertsGroup assertsGroup, Authorities authorities) {
        if (!CollectionUtils.isEmpty(assertsGroup.getAuthorities())) {
            // 判断元素是否存在
            boolean authIsExist = false;
            for (Authorities authEntity : assertsGroup.getAuthorities()) {
                if (authorities.getAuthorityCode().equals(authEntity.getAuthorityCode())) {
                    // 如果已经存在则直接跳出循环
                    authIsExist = true;
                    break;
                }
            }
            // 当资源组与默认权限关系并不存在时则设置为关联关系
            if (!authIsExist) {
                assertsGroup.getAuthorities().add(authorities);
            }
        } else {
            List<Authorities> authorityList = new ArrayList<>();
            authorityList.add(authorities);
            assertsGroup.setAuthorities(authorityList);
        }
        return assertsGroup;
    }

    /**
     * 处理资源对象，用以决定新增处理还是更新处理
     *
     * @param assertsGroup 所属资源组对象
     * @param assetList    被注册过的资源值集合
     * @param authEntity   权限标识对象
     * @param asserts      从数据库中查询出的资源对象，函数中会根据该参数是否为NULL做出不同的逻辑处理
     * @return 返回处理完之后的资源对象
     */
    private Asserts assetHandler(AssertsGroup assertsGroup, List<Asserts> assetList, AuthorizeEntity authEntity, Asserts asserts) {
        // 判断是新增资源还是更新资源
        if (ObjectUtils.isEmpty(asserts)) {
            // 创建新资源时需要检查用户是否有设置个性化资源组，只有在没有设置时才会把资源放入到系统默认资源组中
            // 在这里不需要判断个性化资源组是否存在并创建，在initialization函数中会保证个性化资源组的存在，代码中的断言只做保护作用
            if (!ObjectUtils.isEmpty(authEntity.getGroups())) {
                AssertsGroup customAssetsGroup = this.assertsGroupRepository.findByAssertsGroupNameEquals(authEntity.getGroups());
                Assert.notNull(customAssetsGroup, "注册资源时无法查询到个性化资源组【" + authEntity.getGroups() + "】，可能在初始化个性化资源组数据时出错了，请检查相关数据！");
                asserts = new Asserts(authEntity.getName(), authEntity.getAssetValue(), customAssetsGroup);
            } else {  // 在没有设置个性化资源组的情况下则直接把资源分配至系统默认资源组
                asserts = new Asserts(authEntity.getName(), authEntity.getAssetValue(), assertsGroup);
            }
            // 添加至待处理列表中
            assetList.add(asserts);
        } else {
            asserts.setAssertName(authEntity.getName());
            asserts.setUpdateTime(new Timestamp(System.currentTimeMillis()));
            if (!ObjectUtils.isEmpty(authEntity.getGroups())) {
                AssertsGroup customAssetsGroup = this.assertsGroupRepository.findByAssertsGroupNameEquals(authEntity.getGroups());
                Assert.notNull(customAssetsGroup, "注册资源时无法查询到个性化资源组【" + authEntity.getGroups() + "】，可能在初始化个性化资源组数据时出错了，请检查相关数据！");
                asserts.setAssertsGroup(customAssetsGroup);
            } else {  // 在没有设置个性化资源组的情况下则直接把资源分配至系统默认资源组
                asserts.setAssertsGroup(assertsGroup);
            }
            // 添加至待处理列表中
            assetList.add(asserts);
        }
        return asserts;
    }

    @Transactional
    @Override
    public void deleteScrapAssetsData(String[] excludeAssetsValue) {
        // 找出所有由系统创建的资源组
        List<AssertsGroup> assertsGroupsList = this.assertsGroupRepository.findAllBySystemCreated(true);
        // 遍历所有由系统创建的资源组
        for (AssertsGroup assertsGroup : assertsGroupsList) {
            List<Asserts> notNeedAssetList = this.assertsRepository.findAllByAssertsGroupAndAssertValueNotIn(assertsGroup, excludeAssetsValue);
            // 对于自动注册资源组中的废弃资源数据直接进行删除
            for (Asserts notNeedAsset : notNeedAssetList) {
                String assetJsonStr = null;
                try {
                    assetJsonStr = this.objectMapper.writeValueAsString(notNeedAsset);
                } catch (JsonProcessingException e) {
                    this.logger.warn("对资源【{}】进行移除操作组装打印日志时出错！", notNeedAsset.getAssertName());
                }
                // 删除无用资源
                this.assertsRepository.delete(notNeedAsset);
                // 打印日志信息
                this.logger.info("Removed authority \"{}\"", assetJsonStr);
            }
            // 如果自动注册资源组下已经没有任何资源则将该资源组一并删除
            if (CollectionUtils.isEmpty(assertsGroup.getAsserts())) this.assertsGroupRepository.delete(assertsGroup);
        }
    }

    @Transactional
    @Override
    public void deleteScrapAuthoritiesData(AuthoritiesType authoritiesType, String[] excludeAuthoritiesCode) {
        List<Authorities> notNeedAuthorityList = this.authoritiesRepository.findBySystemCreatedAndAuthoritiesTypeAndAuthorityCodeNotIn(true, authoritiesType, excludeAuthoritiesCode);
        // 判断如果没有把对应权限关联至角色则进行删除操作
        for (Authorities notNeedAuthority : notNeedAuthorityList) {
            // 判断当前权限信息是否能够删除
            if (!CollectionUtils.isEmpty(notNeedAuthority.getRoles())) {
                String roleJsonStr = null;
                try {
                    roleJsonStr = this.objectMapper.writeValueAsString(notNeedAuthority.getRoles());
                } catch (JsonProcessingException e) {
                    this.logger.warn("对权限【{}】进行移除操作组装角色信息打印日志是错误！", notNeedAuthority.getAuthorityName());
                }
                // 打印权限无法删除的警告信息
                this.logger.warn("Authority \"{}\" be assigned to roles \"{}\", can't remove it. Please remove the relationship between permissions and roles first.", notNeedAuthority.getAuthorityName(), roleJsonStr);
            } else {
                // 如果权限与资源组存在无用的关联关系则需要先将关系删除
                if (!CollectionUtils.isEmpty(notNeedAuthority.getAssertsGroups())) {
                    for (AssertsGroup assertsGroup : notNeedAuthority.getAssertsGroups()) {
                        for (Authorities tempAuth : assertsGroup.getAuthorities()) {
                            if (tempAuth.getAuthorityCode().equals(notNeedAuthority.getAuthorityCode())) {
                                assertsGroup.getAuthorities().remove(tempAuth);
                                this.assertsGroupRepository.saveAndFlush(assertsGroup);
                                break;
                            }
                        }
                    }
                }
                // 删除无用权限
                this.authoritiesRepository.delete(notNeedAuthority);
                // 打印日志信息
                this.logger.info("Removed authority \"{}\"", notNeedAuthority.getAuthorityName());
            }
        }
    }
}
